package com.ywz.framework.security;

import cn.hutool.jwt.JWT;
import com.ywz.common.JWTUtils;
import com.ywz.common.StringUtils;
import com.ywz.common.exception.TokenException;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;

/**
 * 类描述 -> Jwt认证过滤器
 *
 * @Author: ywz
 * @Date: 2024/07/28
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 方法描述 -> 在请求之前进行过滤
     *
     * @param request     请求
     * @param response    响应
     * @param filterChain 过滤器链
     * @Author: ywz
     * @Date: 2024/07/28
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // ip限流
        String ipKey = "ip:" + request.getRemoteAddr() + request.getRequestURI();
        if (redisTemplate.hasKey(ipKey)) {
            respInfo(response, "请求过于频繁，请稍后再试");
        } else {
            redisTemplate.opsForValue().set(ipKey, 1, 1, java.util.concurrent.TimeUnit.SECONDS);
        }

        // 获取token
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            // 说明本次请求不需要token，放行
            filterChain.doFilter(request, response);
            return;
        }

        //解析token，获取用户id，权限
        String userid = null;
        String authoritiesStr = null;
        try {
            userid = JWTUtils.getTokenInfoByKey(token, "userId");
            authoritiesStr = JWTUtils.getTokenInfoByKey(token, "authorities");
        } catch (TokenException e) {
            respInfo(response, e.getMessage());
            return;
        }
        if (StringUtils.isEmpty(userid)) {
            respInfo(response, "非法token");
            return;
        }

        // 根据id查询redis中是否存在token，如果不存在，说明token过期了
        String redisKey = "login:" + userid;
        String jwt = (String) redisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isEmpty(jwt)) {
            respInfo(response, "用户登录过期，请重新登录");
            return;
        }

        // 存入SecurityContextHolder
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        if (!StringUtils.isEmpty(authoritiesStr)) {
            for (String authority : authoritiesStr.split(StringUtils.SEPARATOR)) {
                authorities.add(new SimpleGrantedAuthority(authority));
            }
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(jwt, null, authorities);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        // 放行
        filterChain.doFilter(request, response);
    }

    private void respInfo(HttpServletResponse response, String info) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setStatus(200);
        String json = "{\"code\":401,\"data\":\"" + info + "\"}";
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(json.getBytes(StandardCharsets.UTF_8));
        outputStream.close();
    }
}
